Java基础知识(十二)

Author Avatar
子语 2017 - 10 - 23
  • 在其它设备中阅读本文章

辅助概念

关键字final

Java中,final被称为终结器,可以用于定义类、方法、属性,具有如下特点:
1、final定义的类不能有子类

final class A{}
class B extends A{} //报错,无法继承A类

开发系统类或者进行一些架构代码开发时使用到final定义类。
2、final定义的方法不能被方法覆写
父类中某些方法具备某种隐藏的特性,或子类必须使用该方法时,使用final定义,意思是子类不要破坏父类中该方法的结构。

class A{
    public final void fun(){}
}
class B extends A{
    public void fun(){} // 报错,无法覆写方法
}

3、final定义的变量就成了常量。常量必须在定义时设置好值,且不能修改。

class A {
    final double GOOD = 100.0;

    public void fun() {
        GOOD = 200; // 无法修改常量GOOD的值
    }
}

用final定义变量,就可以使用变量名来描述一些数据。开发中常量名均为大写,变量名首个单词小写,之后的单词首字母大写。
4、全局常量:public static final 声明的变量就是全局常量:

public static final String MSG = "MLDN";

static数据存在公共数据区,因此MSG是一个公共常量。

总结:
(1)final定义的类和方法不能继承,不能覆写;
(2)public static final 定义的是全局常量,常量名全部大写。

对象多态性

多态性依赖于方法覆写。

class A {
    public void print() {
        System.out.println("A中的方法");
    }
}

class B extends A {
    public void print() {
        System.out.println("覆写的方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        B b = new B();
        b.print(); // 覆写的方法
    }
}

1、多态性分为两种:

(1)方法的多态性
	|- 方法重载:同一个方法名,根据传入的参数类型或个数的不同,执行不同方法体;
	|- 方法覆写:同一个方法,根据对象所属的子类不同,执行不同方法体。
(2)对象的多态性:指发生在具有继承关系的类中,父类与子类的转换。
	|- 向上转型(自动完成):父类 父类对象 = 子类实例化;
	|- 向下转型(强制完成):子类 子类对象 = (父类)父类实例化;

范例:向上转型

public class Demo {
    public static void main(String[] args) {
        A a = new B();
        a.print(); // 覆写的方法
    }
}

范例:向下转型

public class Demo {
    public static void main(String[] args) {
        B b = (B) new A(); // 向下转型
        b.print(); // 覆写的方法
    }
}

2、向上转型:由于子类对象都可以自动向上转型,所以可应用于参数的统一。

class A {
    public void print() {
        System.out.println("A中的方法");
    }
}

class B extends A {
    public void print() {
        System.out.println("B覆写的方法");
    }
}

class C extends A {
    public void print() {
        System.out.println("C覆写的方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        A a1 = new B(); // 向上转型
        A a2 = new C(); // 向上转型
        a1.print(); // B覆写的方法
        a2.print(); // C覆写的方法
    }
}

a1,a2参数统一后,还可以调用子类覆写的方法,即同一个方法针对不同子类可以有不同实现。
3、向下转型:父类发生向上转型后,不能调用子类中自定义的方法。因此当父类要调用子类自定义方法时,需要进行向下转型,将父类对象变为子类对象。

class A {
}

class B extends A {
    public void fun() {
        System.out.println("自定义方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        A a = new B(); // 向上转型
        a.fun(); // 报错,无法调用B自定义方法
    }
}

向下转型后,可以调用子类自定义的方法:

class A {
}

class B extends A {
    public void fun() {
        System.out.println("自定义方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        A a = new B(); // 向上转型
        B b = (B) a; // 向下转型
        b.fun();
    }
}

问题:上述代码中不适用向下转型,直接实例化子类对象就可以直接调用fun(),为什么还要转型?
答:数据的操作分为两步:设置数据(最需要进行参数统一)和取出数据。

public class Demo {
    public static void main(String[] args) {
        fun(new B()); // 向上转型
    }
    public static void fun(A a){ // 统一参数
        B b = (B) a; // 向下转型
        b.fun(); // 调用子类自定义方法
    }
}

5、个性化操作在开发中尽量少出现,因为对象的强制转型容易带来安全隐患。

class A {
    public void print() {
        System.out.println("A.print()");
    }
}

class B extends A {
    public void print() {
        System.out.println("B.print()");
    }
}

public class Demo {
    public static void main(String[] args) {
        A a = new A();
        B b = (B) a;
        b.print();
    }
}

上述代码会报错,Exception in thread "main" java.lang.ClassCastException: A cannot be cast to B at ExDemo.main(ExDemo.java:15),表示类转换异常,指的是两个没有关系的类对象强制进行向下转型时发生的异常,因此向下转型存在风险。
为保证转型的安全性,Java提供关键字instanceof,其返回结果为boolean:对象 instanceof 类
如果某个对象是某个类的实例,则会返回true,反之返回false。

class A {
    public void print() {
        System.out.println("A.print()");
    }
}

class B extends A {
    public void print() {
        System.out.println("B.print()");
    }
}

public class Demo {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a instanceof A); // true
        System.out.println(a instanceof B); // true
        if (a instanceof B) {
            B b = (B) a;
            b.print(); // B.print()
        }
    }
}

向下转型前应先进行对象的向上转型,建立关系后才能进行向下转型。

总结:
(1)大多数情况只使用向上转型,使得参数统一,便于程序设计;子类尽量覆写方法,而不是自定义方法;
(2)极少情况使用向下转型调用子类的自定义方法,或者不转型。

抽象类

基本概念

1、抽象类是含有抽象方法的类。抽象方法没有方法体,必须用abstract定义。

abstract class A { // 抽象类
    public void fun(){方法体;} // 普通方法
    public abstract void print(); // 抽象方法
}

public class Demo {
    public static void main(String[] args) {
        A a = new A(); // 报错,A是抽象类,无法实例化
    }
}

结果显示,无法直接实例化抽象类对象.普通类的对象实例化后,该对象可以调用类中的属性和方法.而抽象类中存在抽象方法,抽象方法没有方法体无法调用,因此无法产生实例化对象.
2、抽象类使用原则:

(1)抽象类必须有子类;
(2)抽象类的子类(非抽象类时)必须覆写抽象类中所有的抽象方法(强制子类进行方法覆写)
(3)抽象类对象的实例化依靠子类完成,采用向上转型方式。

范例:使用抽象类

abstract class A { // 抽象类
    public void fun() { // 普通方法
        System.out.println("普通方法");
    }

    public abstract void print(); // 抽象方法
}

class B extends A {
    // 强制要求对抽象方法进行覆写,否则会报错
    public void print() {
        System.out.println("覆写后的抽象方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        A a = new B(); // 向上转型
        a.fun();
        a.print();
    }
}

由上述代码,可知:

(1)抽象类的子类明确要求方法覆写,而普通类没有;
(2)抽象类只比普通类多了抽象方法,其他部分相同;
(3)抽象类对象必须经过向上转型才能实例化;
(4)虽然子类可以继承任何类,但开发中,普通类最好继承抽象类。

使用限制

1、抽象类由于存在属性,因此会有构造方法来初始化属性。子类对象实例化时依然先执行父类构造方法,再调用子类构造方法。
2、抽象类不能使用final定义,因为抽象类必须有子类。
3、抽象外部类不允许使用static定义,而抽象内部类可以使用static声明。使用static定义的抽象内部类相当于抽象外部类,继承时使用外部类.内部类的形式表示类名。

abstract class A { // 抽象类
    static abstract class B {
        public abstract void print();
    }
}

class X extends A.B {
    public void print() {
        System.out.println("*****");
    }
}

public class Demo {
    public static void main(String[] args) {
        A.B ab = new X(); // 向上转型
        ab.print();
    }
}

4、static定义的方法可以没有实例化对象的情况下直接调用,即使是抽象类中的static方法。

abstract class A { // 抽象类
    public static void print() {
        System.out.println("static方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        A.print(); // static方法
        A a = new A() ; // 报错
    }
}

5、有时抽象类只需要一个特定的子类操作,因此可以将该子类定义为该抽象类的内部类。

abstract class A { // 抽象类
    public abstract void print();

    private static class B extends A { // 内部抽象类子类
        public void print() {
            System.out.println("Hello");
        }
    }

    public static A getInstance() { // 获取B的实例化对象
        return new B();
    }
}

public class Demo {
    public static void main(String[] args) {
        // 客户端得到抽象类对象时,B对其不可见
        A a = A.getInstance();
        a.print();
    }
}

上述设计在系统类库中较为常见,目的是:为用户隐藏不需要知道的子类。
6、观察下述代码:

abstract class A { // 抽象类
    // 1. 先调用父类构造
    public A() {
        this.print();
    }

    public abstract void print();
}

class B extends A {
    private int num = 100;
    // 3.调用子类构造,并初始化num = 30.运行结束,未输出初始化后的num
    public B(int num) {
        this.num = num;
    }
    // 2. 父类构造调用子类的print,此时num = 0,打印输出。
    public void print() {
        System.out.println("num = " + num);
    }
}

public class Demo {
    public static void main(String[] args) {
        new B(30); // 4. 结果为0
    }
}

在构造方法执行完之前,属性的内容均为其对应的数据类型的默认值。子类在执行构造方法前必先执行父类的构造方法,因为此时子类构造方法还没执行,就调用print()输出了num的值,所以num为0.

模板设计模式

要求:设计三个类,通过类描述如下行为:
(1)机器人:充电、工作;
(2)人:吃饭、工作、睡觉;
(3)猪:吃饭、睡觉
思路:定义一个抽象类,具有吃饭、睡觉、工作的抽象方法。根据子类的不同,具体实现抽象方法。
无法加载

abstract class Action {
    public static final int EAT = 1;
    public static final int SLEEP = 5;
    public static final int WORK = 7;

    public void command(int flag) {
        switch (flag) {
            case EAT:
                this.eat();
                break;
            case SLEEP:
                this.sleep();
                break;
            case WORK:
                this.work();
                break;
            case EAT + WORK:
                this.eat();
                this.work();
                break;
        }
    }
    // 不确定方法中的具体行为,定义为抽象类
    public abstract void eat();

    public abstract void sleep();

    public abstract void work();
}

定义子类

class Robot extends Action {
    public void eat() {
        System.out.println("机器人正在补充能量");
    }

    public void sleep() {
    }

    public void work() {
        System.out.println("机器人正在工作");
    }
}

class Human extends Action {
    public void eat() {
        System.out.println("人正在吃饭");
    }

    public void sleep() {
        System.out.println("人正在睡觉");
    }

    public void work() {
        System.out.println("人正在工作");
    }
}

class Pig extends Action {
    public void eat() {
        System.out.println("猪正在吃饭");
    }

    public void sleep() {
        System.out.println("猪正在睡觉");
    }

    public void work() {
    }
}

范例:测试程序

public class Demo {
    public static void main(String[] args) {
        fun(new Robot());
        fun(new Human());
        fun(new Pig());
    }

    public static void fun(Action act) {
        act.command(Action.EAT);
        act.command(Action.SLEEP);
        act.command(Action.WORK);
    }
}

结果显示:子类要实现操作,必须按照Action类的标准。

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/Java/Java Base/Java基础知识(十二)/